home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Languages Suite
/
ProgramD2.iso
/
Borland
/
Borland C++ V5.02
/
MTRECAL.PAK
/
RECALDOC.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1997-05-06
|
9KB
|
316 lines
// recaldoc.cpp : implementation of the CRecalcDoc class
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.
#include "stdafx.h"
#include "mtrecalc.h"
#include "calcthrd.h"
#include "recaldoc.h"
#include "slowcalc.h"
#include "speeddlg.h"
#include "recalcvw.h"
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CRecalcDoc
IMPLEMENT_DYNCREATE(CRecalcDoc, CDocument)
BEGIN_MESSAGE_MAP(CRecalcDoc, CDocument)
ON_COMMAND_RANGE(ID_SINGLE_THREAD, ID_UI_THREAD, OnDemoThread)
ON_UPDATE_COMMAND_UI_RANGE(ID_SINGLE_THREAD, ID_UI_THREAD, OnUpdateDemoThread)
//{{AFX_MSG_MAP(CRecalcDoc)
ON_COMMAND(ID_RECALC_SPEED, OnRecalcSpeed)
ON_COMMAND(ID_KILL_WORKER_THREAD, OnKillWorkerThread)
ON_UPDATE_COMMAND_UI(ID_KILL_WORKER_THREAD, OnUpdateKillWorkerThread)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CRecalcDoc construction/destruction
CRecalcDoc::CRecalcDoc()
{
m_nInt1 = m_nInt2 = m_nSum = 0;
m_bRecalcNeeded = m_bRecalcInProgress = FALSE;
m_nRecalcSpeedSeconds = 5;
m_nCurrentDemoCommand = ID_SINGLE_THREAD;
m_hEventStartRecalc = CreateEvent(NULL, FALSE, FALSE, NULL); // auto reset, initially reset
m_hEventRecalcDone = CreateEvent(NULL, TRUE, TRUE, NULL); // manual reset, initially set
m_hEventKillRecalcThread = CreateEvent(NULL, FALSE, FALSE, NULL); // auto reset, initially reset
m_hEventRecalcThreadKilled = CreateEvent(NULL, FALSE, FALSE, NULL); // auto reset, initially reset
m_recalcThreadInfo.m_hEventStartRecalc = m_hEventStartRecalc;
m_recalcThreadInfo.m_hEventRecalcDone = m_hEventRecalcDone;
m_recalcThreadInfo.m_hEventKillRecalcThread = m_hEventKillRecalcThread;
m_recalcThreadInfo.m_hEventRecalcThreadKilled = m_hEventRecalcThreadKilled;
m_pRecalcWorkerThread = NULL;
}
CRecalcDoc::~CRecalcDoc()
{
// In this application, the document owns the worker thread.
// The document's destructor is responsible for killing the active worker
// thread.
// It's a good idea to wait for the worker thread to notify via a
// "thread killed" event that it has killed itself. Otherwise, in the case
// where the app is terminating, is possible (even if unlikely) that it
// will detect a memory leak of the CWinThread object before the
// CWinThread object has had a chance to auto-delete itself.
DWORD dwExitCode;
if (m_pRecalcWorkerThread != NULL &&
GetExitCodeThread(m_pRecalcWorkerThread->m_hThread, &dwExitCode) &&
dwExitCode == STILL_ACTIVE)
{
// Kill the worker thread by setting the "kill thread" event.
// See comment in OnKillWorkerThread for explanation of the sequence
// of the "kill thread" and "start recalc" events.
SetEvent(m_hEventKillRecalcThread);
SetEvent(m_hEventStartRecalc);
WaitForSingleObject(m_hEventRecalcThreadKilled, INFINITE);
}
}
/////////////////////////////////////////////////////////////////////////////
// CRecalcDoc overrides
BOOL CRecalcDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
return TRUE;
}
BOOL CRecalcDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
if (m_bRecalcInProgress)
{
if (AfxMessageBox(IDS_CONTINUE_RECALC_BEFORE_SAVE, MB_YESNO) == IDYES)
{
BeginWaitCursor();
WaitForSingleObject(m_hEventRecalcDone, INFINITE);
RecalcDone();
EndWaitCursor();
}
else
{
AfxMessageBox(IDS_FILE_NOT_SAVED);
return FALSE;
}
}
else if (m_bRecalcNeeded) // recalc needed but not in progress
{
if (AfxMessageBox(IDS_NEED_RECALC_BEFORE_SAVE, MB_YESNO) == IDYES)
{
BeginWaitCursor();
// Update the document from data in the view and start recalculation.
POSITION pos = GetFirstViewPosition();
ASSERT(pos != NULL);
CView* pView = GetNextView(pos);
pView->SendMessage(WM_USER_RECALC_NOW, 0, 0);
WaitForSingleObject(m_hEventRecalcDone, INFINITE);
RecalcDone();
EndWaitCursor();
}
else
{
AfxMessageBox(IDS_FILE_NOT_SAVED);
return FALSE;
}
}
return CDocument::OnSaveDocument(lpszPathName);
}
/////////////////////////////////////////////////////////////////////////////
// CRecalcDoc serialization
void CRecalcDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
ar << (WORD)m_nInt1;
ar << (WORD)m_nInt2;
ar << (WORD)m_nSum;
}
else
{
WORD w;
ar >> w;
m_nInt1 = w;
ar >> w;
m_nInt2 = w;
ar >> w;
m_nSum = w;
}
}
/////////////////////////////////////////////////////////////////////////////
// CRecalcDoc diagnostics
#ifdef _DEBUG
void CRecalcDoc::AssertValid() const
{
CDocument::AssertValid();
}
void CRecalcDoc::Dump(CDumpContext& dc) const
{
CDocument::Dump(dc);
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CRecalcDoc operations and implementation
void CRecalcDoc::UpdateInt1AndInt2(int n1, int n2, BOOL bForceRecalc)
{
if (!bForceRecalc && m_nInt1 == n1 && m_nInt2 == n2)
return; // recalculation not needed
SetModifiedFlag();
m_nInt1 = n1;
m_nInt2 = n2;
m_bRecalcNeeded = m_bRecalcInProgress = TRUE;
// Synchronize the int1 and int2 fields in the other views,
// and show "recalculating..." in the sum field.
UpdateAllViews(NULL);
switch (m_nCurrentDemoCommand)
{
case ID_SINGLE_THREAD:
RecalcInSingleThread();
break;
case ID_WORKER_THREAD:
RecalcInSecondThread();
break;
default:
ASSERT(FALSE);
}
}
void CRecalcDoc::RecalcInSingleThread()
{
SlowAdd(m_nInt1, m_nInt2, m_nSum, NULL, m_nRecalcSpeedSeconds, AfxGetMainWnd()->m_hWnd);
m_bRecalcNeeded = m_bRecalcInProgress = FALSE;
// Update the "sum" field.
UpdateAllViews(NULL, UPDATE_HINT_SUM);
}
void CRecalcDoc::RecalcInSecondThread()
{
if (m_pRecalcWorkerThread == NULL)
{
// Begin the worker thread. It is ok to fill in the CThreadInfo
// structure after the thread has started, because the thread
// waits for the "start recalc" event before referring to the structure.
m_pRecalcWorkerThread =
AfxBeginThread(RecalcThreadProc, &m_recalcThreadInfo);
}
m_recalcThreadInfo.m_nInt1 = m_nInt1;
m_recalcThreadInfo.m_nInt2 = m_nInt2;
POSITION pos = GetFirstViewPosition();
ASSERT(pos != NULL);
CView* pView = GetNextView(pos);
ASSERT(pView != NULL);
m_recalcThreadInfo.m_hwndNotifyRecalcDone = pView->m_hWnd;
m_recalcThreadInfo.m_hwndNotifyProgress = AfxGetMainWnd()->m_hWnd;
m_recalcThreadInfo.m_nRecalcSpeedSeconds = m_nRecalcSpeedSeconds;
// The events are initially set or reset in the CreateEvent call;
// but they may be left in an improperly initialized state if
// a worker thread has been previously started and then prematurely
// killed. Set/reset the events to the proper initial state.
// Set the "start recalc" event last, since it is the event the
// triggers the starting of the worker thread recalculation.
SetEvent(m_hEventRecalcDone);
ResetEvent(m_hEventKillRecalcThread);
ResetEvent(m_hEventRecalcThreadKilled);
SetEvent(m_hEventStartRecalc);
// RecalcDone() will be called by the view when the thread sends a
// WM_USER_RECALC_DONE message.
}
void CRecalcDoc::RecalcDone()
{ // Called by the view when the worker thread sends a WM_USER_RECALC_DONE message.
m_nSum = m_recalcThreadInfo.m_nSum;
m_bRecalcNeeded = m_bRecalcInProgress = FALSE;
UpdateAllViews(NULL, UPDATE_HINT_SUM);
}
/////////////////////////////////////////////////////////////////////////////
// CRecalcDoc commands
void CRecalcDoc::OnDemoThread(UINT nCmdID)
{
m_nCurrentDemoCommand = nCmdID;
}
void CRecalcDoc::OnUpdateDemoThread(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(pCmdUI->m_nID == m_nCurrentDemoCommand);
}
void CRecalcDoc::OnRecalcSpeed()
{
CSpeedDlg dlg;
dlg.m_nRecalcSpeedSeconds = m_nRecalcSpeedSeconds;
if (dlg.DoModal() == IDOK)
m_nRecalcSpeedSeconds = dlg.m_nRecalcSpeedSeconds;
}
void CRecalcDoc::OnKillWorkerThread()
{
// The worker thread periodically checks for the "kill recalc" event
// during its execution of a recalculation. When the worker thread
// is not currently recalculating, it is waiting for a "start recalc"
// event. Therefore, in order to kill the thread that might be waiting
// for the "start recalc" event, it is necessary to first set the
// "start recalc" event.
SetEvent(m_hEventKillRecalcThread);
SetEvent(m_hEventStartRecalc);
WaitForSingleObject(m_hEventRecalcThreadKilled, INFINITE);
m_pRecalcWorkerThread = NULL;
m_bRecalcInProgress = FALSE; // but m_bRecalcNeeded is still TRUE
UpdateAllViews(NULL, UPDATE_HINT_SUM);
}
void CRecalcDoc::OnUpdateKillWorkerThread(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_nCurrentDemoCommand == ID_WORKER_THREAD
&& m_pRecalcWorkerThread != NULL);
}